package me.zhengjie.modules.poker;

import cn.hutool.core.util.RandomUtil;

import java.util.*;
import java.util.stream.Collectors;

public class PokerFormatUtil {

    static class HandResult {
        private final HandType handType;
        private final List<Integer> kickerValues;

        HandResult(HandType handType, List<Integer> kickerValues) {
            this.handType = handType;
            this.kickerValues = kickerValues;
        }

        HandType getHandType() {
            return handType;
        }

        List<Integer> getKickerValues() {
            return kickerValues;
        }
    }


    public static void evaluatePlayers(List<Player> players) {
        List<HandResult> results = new ArrayList<>();

        for (Player player : players) {
            HandResult result = evaluateHand(player.getCards());
            results.add(result);
        }

        // Sort players based on hand type and kickers
        List<Pair<Player, HandResult>> pairs = new ArrayList<>();
        for (int i = 0; i < players.size(); i++) {
            pairs.add(new Pair<>(players.get(i), results.get(i)));
        }

        pairs.sort((pair1, pair2) -> {
            int comparison = Integer.compare(pair2.getValue().getHandType().ordinal(), pair1.getValue().getHandType().ordinal());
            if (comparison != 0) {
                return comparison;
            }

            List<Integer> kickers1 = pair1.getValue().getKickerValues();
            List<Integer> kickers2 = pair2.getValue().getKickerValues();

            for (int i = 0; i < Math.min(kickers1.size(), kickers2.size()); i++) {
                comparison = Integer.compare(kickers2.get(i), kickers1.get(i));
                if (comparison != 0) {
                    return comparison;
                }
            }

            // If all kickers are equal, compare remaining high cards
            Collections.sort(pair1.getKey().getCards(), Comparator.comparingInt((Card c) -> c.getRank().getValue()).reversed());
            Collections.sort(pair2.getKey().getCards(), Comparator.comparingInt((Card c) -> c.getRank().getValue()).reversed());

            for (int i = 0; i < 5; i++) {
                comparison = Integer.compare(pair2.getKey().getCards().get(i).getRank().getValue(), pair1.getKey().getCards().get(i).getRank().getValue());
                if (comparison != 0) {
                    return comparison;
                }
            }

            return 0;
        });

        for (int i = 0; i < pairs.size(); i++) {
            Player player = pairs.get(i).getKey();
            HandResult result = pairs.get(i).getValue();
            player.setRank(i + 1);
            player.setHandType(result.getHandType().getChineseName());
        }
    }

    private static HandResult evaluateHand(List<Card> cards) {
        Collections.sort(cards);
        boolean isFlush = isFlush(cards);
        boolean isStraight = isStraight(cards);

        Map<Integer, Long> rankCounts = cards.stream()
                .collect(Collectors.groupingBy(card -> card.getRank().getValue(), Collectors.counting()));

        long fourOfAKindCount = rankCounts.values().stream().filter(count -> count == 4).count();
        long threeOfAKindCount = rankCounts.values().stream().filter(count -> count == 3).count();
        long pairCount = rankCounts.values().stream().filter(count -> count == 2).count();

        if (isFlush && isStraight) {
            if (cards.get(4).getRank() == Rank.ACE) {
                return new HandResult(HandType.ROYAL_FLUSH, Collections.emptyList());
            } else {
                return new HandResult(HandType.STRAIGHT_FLUSH, Collections.singletonList(cards.get(4).getRank().getValue()));
            }
        } else if (fourOfAKindCount == 1) {
            return new HandResult(HandType.FOUR_OF_A_KIND, extractRanksWithCount(rankCounts, 4));
        } else if (threeOfAKindCount == 1 && pairCount == 1) {
            return new HandResult(HandType.FULL_HOUSE, extractRanksWithCount(rankCounts, 3));
        } else if (isFlush) {
            return new HandResult(HandType.FLUSH, cards.stream().map(card -> card.getRank().getValue()).toList());
        } else if (isStraight) {
            return new HandResult(HandType.STRAIGHT, Collections.singletonList(cards.get(4).getRank().getValue()));
        } else if (threeOfAKindCount == 1) {
            return new HandResult(HandType.THREE_OF_A_KIND, extractRanksWithCount(rankCounts, 3));
        } else if (pairCount == 2) {
            return new HandResult(HandType.TWO_PAIR, extractRanksWithCount(rankCounts, 2));
        } else if (pairCount == 1) {
            return new HandResult(HandType.ONE_PAIR, extractRanksWithCount(rankCounts, 2));
        } else {
            return new HandResult(HandType.HIGH_CARD, cards.stream().map(card -> card.getRank().getValue()).toList());
        }
    }

    private static boolean isFlush(List<Card> cards) {
        Suit suit = cards.get(0).getSuit();
        for (Card card : cards) {
            if (card.getSuit() != suit) {
                return false;
            }
        }
        return true;
    }

    private static boolean isStraight(List<Card> cards) {
        for (int i = 1; i < cards.size(); i++) {
            if (cards.get(i).getRank().getValue() != cards.get(i - 1).getRank().getValue() + 1) {
                return false;
            }
        }
        return true;
    }

    private static List<Integer> extractRanksWithCount(Map<Integer, Long> rankCounts, long count) {
        return rankCounts.entrySet().stream()
                .filter(entry -> entry.getValue() == count)
                .sorted((e1, e2) -> Integer.compare(e2.getKey(), e1.getKey()))
                .map(Map.Entry::getKey)
                .limit(1)
                .toList();
    }

    static class Pair<K, V> {
        private final K key;
        private final V value;

        Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }

        K getKey() {
            return key;
        }

        V getValue() {
            return value;
        }
    }

    public static String getRandomCard(String Card1,String Card2){
//        spades("黑桃"), clubs("方片"), hearts("红心"), diamonds("梅花");
        ArrayList<String> typeList = new ArrayList<>();
        typeList.add("黑桃");
        typeList.add("方片");
        typeList.add("红心");
        typeList.add("梅花");
        ArrayList<String> numList = new ArrayList<>();
        numList.add("A");
        numList.add("2");
        numList.add("3");
        numList.add("4");
        numList.add("5");
        numList.add("6");
        numList.add("7");
        numList.add("8");
        numList.add("9");
        numList.add("10");
        numList.add("J");
        numList.add("Q");
        numList.add("K");
        String type = RandomUtil.randomEle(typeList);
        String num = RandomUtil.randomEle(numList);
        String card = type +"-"+ num;
        if (card.equals(Card1)||card.equals(Card2)){
            return getRandomCard(Card1,Card2);
        }else{
            return card;
        }
    }
    public static String getRandomCard(){
//        spades("黑桃"), clubs("方片"), hearts("红心"), diamonds("梅花");
        ArrayList<String> typeList = new ArrayList<>();
        typeList.add("黑桃");
        typeList.add("方片");
        typeList.add("红心");
        typeList.add("梅花");
        ArrayList<String> numList = new ArrayList<>();
        numList.add("A");
        numList.add("2");
        numList.add("3");
        numList.add("4");
        numList.add("5");
        numList.add("6");
        numList.add("7");
        numList.add("8");
        numList.add("9");
        numList.add("10");
        numList.add("J");
        numList.add("Q");
        numList.add("K");
        String type = RandomUtil.randomEle(typeList);
        String num = RandomUtil.randomEle(numList);
        return type +"-"+ num;

    }
}